home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / wais / ir / irretrvl.c < prev    next >
C/C++ Source or Header  |  1995-05-09  |  16KB  |  559 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.    
  4.   
  5. */
  6.  
  7. #ifndef lint
  8. static char *RCSid = "$Header: /tmp_mnt/net/quake/proj/wais/wais-8-b5/ir/RCS/irretrvl.c,v 1.30 92/05/10 14:43:59 jonathan Exp $";
  9. #endif
  10.  
  11. /* Change log:
  12.  * $Log:    irretrvl.c,v $
  13.  * Revision 1.30  92/05/10  14:43:59  jonathan
  14.  * 
  15.  * Made a little safer on NULL docid's when parsing.
  16.  * 
  17.  * Revision 1.29  92/05/06  17:31:26  jonathan
  18.  * modified #if's for NeXT and Mach.  Added S_ISDIR definition for them both.
  19.  * 
  20.  * Revision 1.28  92/05/04  17:19:54  jonathan
  21.  * Added test for parsing docids (if null, log error).
  22.  * 
  23.  * Revision 1.27  92/04/28  16:56:08  morris
  24.  * added boolean to serial engine
  25.  * 
  26.  * Revision 1.26  92/04/01  17:09:46  jonathan
  27.  * Added index_directory to check_for_legitimate_file to test if filename is
  28.  * under default directory (for FTP-like retrieval).
  29.  * 
  30.  * 
  31.  * Revision 1.25  92/03/18  08:54:41  jonathan
  32.  * Removed databaseName argument from getData and getDocumentText.  The
  33.  * database name is now culled from the docid.  Removed special cases for INFO
  34.  * and Quest db's, as they should no longer be needed.
  35.  * 
  36.  * Revision 1.24  92/02/18  14:04:49  jonathan
  37.  * in check_for_legitimate_file: added INFO to the list of special case
  38.  * retrievals from MAC's.
  39.  * 
  40.  * Revision 1.23  92/02/18  11:53:45  jonathan
  41.  * conditionalized use of tempnam for NeXT (doesn't exist, use tmpnam
  42.  * instead).  May be a BSD thing.
  43.  * 
  44.  * Revision 1.22  92/02/17  12:38:52  jonathan
  45.  * special case catalog in check_for_legitimate_file.
  46.  * 
  47.  * Revision 1.21  92/02/16  18:04:52  jonathan
  48.  * Demoted more WLOG_ERROR's to WLOG_WARNING's
  49.  * 
  50.  * Revision 1.20  92/02/15  19:40:30  jonathan
  51.  * Improved reporting of retrieval errors.
  52.  * 
  53.  * Revision 1.19  92/02/15  18:58:38  jonathan
  54.  * Changed most (but not all) waislog errors to warnings on retrieval.
  55.  * 
  56.  * Revision 1.18  92/02/14  16:06:20  jonathan
  57.  * Fixed text in error message for invalid docid (not in DB)
  58.  * 
  59.  * Revision 1.17  92/02/14  15:24:08  jonathan
  60.  * Made parseDocID public.
  61.  * 
  62.  * Revision 1.16  92/02/12  13:29:35  jonathan
  63.  * Added "$Log" so RCS will put the log message in the header
  64.  * 
  65. */
  66.  
  67. /* retrieval part of the serial ir engine.  if you are using a different 
  68.    storage system for the documents, replace this file.
  69.  
  70.    -brewster
  71.  
  72.  10/91 added .Z file support from mlm@cs.brown.edu (Moises Lejter)
  73.  
  74.  to do:
  75.   handle .Z files at a lower level.
  76.  
  77.  */
  78.  
  79. #include "irretrvl.h"
  80. #include "irfiles.h" /* for filename_table_ext */
  81. #include <string.h>
  82. #include "futil.h"
  83. #include <ctype.h>  /* for isspace */
  84. #include "irext.h"
  85. #include "irdirent.h"
  86. #include <sys/stat.h>
  87.  
  88. #ifdef Mach
  89. #include <sys/inode.h>
  90. #define S_ISDIR(f_mode) (f_mode & IFDIR)
  91. #endif /* Mach */
  92.  
  93. #if (defined(NeXT) && !(defined(S_ISDIR)))
  94. #define S_ISDIR(f_mode) ((fmode) & S_IFDIR)
  95. #endif
  96.  
  97. /*----------------------------------------------------------------------*/
  98.  
  99.  
  100. boolean
  101. parseDocID(doc,filename,start_character,end_character,errorCode)
  102. DocObj* doc;
  103. char* filename;
  104. long* start_character;
  105. long* end_character;
  106. long* errorCode;
  107. {
  108.   DocID* theDocID = NULL;
  109.   char* local_id = NULL;
  110.   char* token = NULL;
  111.   long i;
  112.  
  113.   if((theDocID = docIDFromAny(doc->DocumentID)) == NULL) 
  114.     return false;
  115.  
  116.   local_id = anyToString(GetLocalID(theDocID));
  117.   
  118.   freeDocID(theDocID);
  119.  
  120.   /* parse the doc id into start pos, end pos, and filename */
  121.   /* first the start char */
  122.   token = local_id;
  123.   for (i = 0; local_id[i] != '\0' && isspace(local_id[i]) == false; i++)
  124.     ;
  125.   if (local_id[i] == '\0')
  126.    { 
  127.      waislog(WLOG_HIGH, WLOG_WARNING, 
  128.          "Attempt to retrieve data for bad doc-id: '%s'",local_id); 
  129.      *errorCode = GDT_BadDocID;
  130.      s_free(local_id);
  131.      return(false);
  132.    }
  133.   local_id[i] = '\0';
  134.   sscanf(token,"%ld",start_character);
  135.   /* now the second char */
  136.   token = local_id + i + 1;
  137.   for (++i; local_id[i] != '\0' && isspace(local_id[i]) == false; i++)
  138.    ;
  139.   if (local_id[i] == '\0')
  140.    { 
  141.      waislog(WLOG_HIGH, WLOG_WARNING, 
  142.          "Attempt to retrieve data for bad doc-id: '%s'",local_id); 
  143.      *errorCode = GDT_BadDocID;
  144.      s_free(local_id);
  145.      return(false);
  146.    }
  147.   local_id[i] = '\0';
  148.   sscanf(token,"%ld",end_character);
  149.   /* and finally the file name */
  150.   strncpy(filename,local_id + i + 1,MAX_FILENAME_LEN);
  151.   s_free(local_id);
  152.   return(true);
  153. }
  154.  
  155.  
  156. /*----------------------------------------------------------------------*/
  157.  
  158. /* this checks to make sure that the filename is a file 
  159.    within the database */
  160.  
  161. static boolean check_for_legitimate_file 
  162.   _AP((char *filename, char* database_name, char* index_directory));
  163.  
  164. static boolean check_for_legitimate_file(filename, database_name, index_directory)
  165.      char *filename;
  166.      char *database_name;  /* full pathname of the database */
  167.      char *index_directory;
  168. {
  169.   struct stat sbuf;
  170.  
  171.   /* the help file and catalog file (the .src and .cat files) must be
  172.      special cased because it is not in the filename table */
  173.  
  174.   /* caching is done in filename_in_filename_file for repeated requests 
  175.      for the same file, so it does not need to be repeated here. */
  176.  
  177.   if(NULL != strstr(filename, ".src")) /* let it pass */
  178.     return(true);
  179.  
  180.   if(NULL != strstr(filename, ".cat")) /* let it pass */
  181.     return(true);
  182.  
  183.   stat(filename, &sbuf);
  184.   if(S_ISDIR(sbuf.st_mode)) {
  185.     waislog(WLOG_HIGH, WLOG_WARNING, 
  186.         "File: '%s' is a directory, and cannot be retrieved.",
  187.         filename);
  188.     return(false);
  189.   }
  190.   else {
  191.     /* name of the file of the filetable for this db (eg  /bar/foo.fn).  confusing, no? */
  192.     char filename_table_filename[MAX_FILE_NAME_LEN +1]; 
  193.     
  194.     pathname_directory(database_name, filename_table_filename);
  195.     strncat(filename_table_filename, "/", MAX_FILE_NAME_LEN);
  196.     strncat(filename_table_filename, 
  197.         database_file(pathname_name(database_name)), 
  198.         MAX_FILE_NAME_LEN);
  199.     s_strncat(filename_table_filename, filename_table_ext, MAX_FILE_NAME_LEN,
  200.           MAX_FILE_NAME_LEN);
  201.     if(!filename_in_filename_file(filename, NULL, NULL, filename_table_filename)){
  202.       /* we lose.  this means either the db does not exist, or
  203.      the file is not in that db.  Log the bad news */
  204.       if(index_directory == NULL)
  205.     return true;
  206.       else if (substrcmp(filename, index_directory))
  207.     return true;
  208.       waislog(WLOG_HIGH, WLOG_WARNING, 
  209.           "File: '%s' is not in DB '%s', and cannot be retrieved.",
  210.           filename, filename_table_filename);
  211.       return(false);
  212.     }
  213.     else{            /* everything is peachy */
  214.       return(true);
  215.     }
  216.   }
  217. }
  218.   
  219.  
  220. /*----------------------------------------------------------------------*/
  221.  
  222. WAISDocumentText* getData(doc, errorCode, index_directory)
  223. DocObj* doc;
  224. long* errorCode;
  225. char* index_directory;
  226. /* it isn't text, so we can just grab data */
  227. {
  228.   FILE* file = NULL;
  229.   char fileName[MAX_FILENAME_LEN + 1];
  230.   char* dbname = NULL;
  231.   WAISDocumentText* data = NULL;
  232.   long start,end;        /* position of the document in the file */
  233.   long startByte,endByte,bytes,bytesRead; /* part of the doc that we want */
  234.   char* buffer = NULL;
  235.   any* bufAny = NULL;
  236.   DocID *docid;
  237. #if (defined(NeXT) || defined(Mach))
  238.   char tmpFileName[MAX_FILENAME_LEN+1];
  239. #else
  240.   char *tmpFileName = NULL;
  241. #endif /* NeXT or Mach */
  242.  
  243.   /* we can only handle byte chunks here */
  244.   if ((doc->ChunkCode == CT_byte) ||
  245.       (doc->ChunkCode == CT_document)) {
  246.     if (parseDocID(doc,fileName,&start,&end,errorCode) == false)
  247.      {
  248.        waislog(WLOG_HIGH, WLOG_WARNING, "can't parse docid");
  249.        *errorCode = GDT_MissingDocID;
  250.        return(NULL);
  251.      }
  252.  
  253.     *errorCode = GDT_NoError;
  254.   
  255.     docid = docIDFromAny(doc->DocumentID);
  256.     dbname = anyToString(GetDatabase(docid));
  257.     freeDocID(docid);
  258.  
  259.     if(true == check_for_legitimate_file(fileName, dbname, index_directory)){
  260.       file = s_fopen(fileName,"rb"); 
  261.  
  262.       if (file == NULL){
  263.     if(probe_file_possibly_compressed(fileName)) {
  264.       char buffer[ 2 * MAX_FILENAME_LEN + 10 ];
  265. #if (defined(NeXT) || defined(Mach))
  266.       tmpnam(tmpFileName);
  267. #else
  268.       tmpFileName = tempnam( "/tmp/", 0 );
  269. #endif /* NeXT or Mach */
  270.       sprintf( buffer, "zcat %s.Z > %s", fileName, tmpFileName );
  271.       system( buffer );
  272.       file = s_fopen(tmpFileName,"rb");
  273.     }
  274.       }
  275.     }
  276.  
  277.     if (file == NULL) { 
  278.       waislog(WLOG_HIGH, WLOG_WARNING, 
  279.           "Attempt to retrieve data for missing doc-id: '%s'",
  280.           fileName);
  281.       *errorCode = GDT_MissingDocID;
  282.       s_free(dbname);
  283.       return(NULL);
  284.     }
  285.  
  286.     if (doc->ChunkCode == CT_byte) {
  287.       startByte = doc->ChunkStart.Pos + start;
  288.       endByte = doc->ChunkEnd.Pos + start;
  289.     }
  290.     else {
  291.       startByte = start;
  292.       endByte = end;
  293.     }
  294.  
  295.     waislog(WLOG_LOW, WLOG_RETRIEVE,
  296.         "Retrieving DocID: %d %d %s, byte: %d %d, from database %s", 
  297.         start, end, fileName, startByte, endByte, dbname);
  298.  
  299.     s_free(dbname);
  300.  
  301.     if (endByte > end && end != 0) { 
  302.       waislog(WLOG_HIGH, WLOG_WARNING, 
  303.           "retrieval beyond bounds of document %ld in file <%s>",
  304.           endByte,fileName);
  305.       *errorCode = GDT_BadRange;
  306.       endByte = end;
  307.     }
  308.    
  309.     /* get the bytes */
  310.     if (fseek(file,startByte,SEEK_SET) != 0)
  311.       { 
  312.     waislog(WLOG_HIGH, WLOG_WARNING, 
  313.         "retrieval can't seek to %ld in file <%s>",startByte,
  314.         fileName);
  315.     *errorCode = GDT_BadRange;
  316.     if (tmpFileName) unlink( tmpFileName );
  317.     if (tmpFileName) unlink( tmpFileName );
  318.     if (tmpFileName) unlink( tmpFileName );
  319.     return(NULL);
  320.       }
  321.  
  322.     bytes = endByte - startByte; 
  323.     buffer = (char*)s_malloc(bytes);
  324.   
  325.     bytesRead = fread((void*)buffer,(size_t)sizeof(char),bytes,file);
  326.   
  327.     if (bytesRead != bytes)
  328.       { 
  329.     waislog(WLOG_HIGH, WLOG_WARNING, 
  330.         "retrieval error in file <%s>",fileName);
  331.     *errorCode = GDT_BadRange;
  332.     if (bytesRead == 0) 
  333.       return(NULL);
  334.       }
  335.   
  336.     bufAny = makeAny(bytesRead,buffer);
  337.   
  338.     data = makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  339.   
  340.     /* the any and the buffer are freed by freeWAISSearchResponse() */
  341.     s_fclose(file);
  342.     if (tmpFileName) unlink( tmpFileName );
  343.  
  344.     return(data);
  345.   }
  346.   else
  347.     { 
  348.       waislog(WLOG_HIGH, WLOG_WARNING, 
  349.           "search engine can only use whole documents or byte offsets for data lookup");
  350.       *errorCode = GDT_UnsupportedChunkType;
  351.       return(NULL);
  352.     }
  353.  
  354. }
  355.  
  356. /*----------------------------------------------------------------------*/
  357.  
  358. #define BUFSZ    (size_t)5000
  359.  
  360. WAISDocumentText* getDocumentText(doc, errorCode, index_directory)
  361. DocObj* doc;
  362. long* errorCode;
  363. char* index_directory;
  364. /* find the text for doc, get the sub part if any, finally construct and
  365.    return a WAISDocumentText.  If it can not find the document 
  366.    (or some other error) it returns NULL and sets errorCode.
  367.  */
  368. {
  369.   WAISDocumentText* text = NULL;
  370.   FILE* file = NULL;
  371.   char* dbname = NULL;
  372.   char* buffer = NULL;
  373.   any* bufAny = NULL;
  374.   char filename[MAX_FILENAME_LEN + 1];
  375.   long start_character;
  376.   long end_character;
  377.   register long i;
  378.   long bytes,bytesRead;
  379.   long startByte,endByte,byte,lines;
  380. #if (defined(NeXT) || defined(Mach))    
  381.   char tmpFileName[MAX_FILENAME_LEN+1];
  382. #else
  383.   char *tmpFileName = NULL;
  384. #endif /* NeXT or Mach */
  385.   DocID* theDocID = NULL;
  386.   char* local_id = NULL;
  387.   
  388.   *errorCode = GDT_NoError;
  389.  
  390.   /* we can only handle line chunks for now */
  391.   if (doc->ChunkCode != CT_line)
  392.    { 
  393.      waislog(WLOG_HIGH, WLOG_WARNING, 
  394.          "search engine can only use line offsets for now.");
  395.      *errorCode = GDT_UnsupportedChunkType;
  396.      return(NULL);
  397.    }
  398.  
  399.   theDocID = docIDFromAny(doc->DocumentID);
  400.   dbname = anyToString(GetDatabase(theDocID));
  401.   local_id = anyToString(GetLocalID(theDocID));
  402.   freeDocID(theDocID);
  403.  
  404.   if (parseDocID(doc,filename,&start_character,&end_character,errorCode) == 
  405.       false) {
  406.     waislog(WLOG_HIGH, WLOG_ERROR,
  407.         "Can't parse doc-id: '%s'", local_id);
  408.     *errorCode = GDT_MissingDocID;
  409.     s_free(dbname);
  410.     s_free(local_id);
  411.     return(NULL);
  412.   }
  413.  
  414.   waislog(WLOG_LOW, WLOG_RETRIEVE,
  415.       "Retrieving DocID: '%s', line range: %d %d, from database %s", 
  416.       local_id, doc->ChunkStart.Pos, doc->ChunkEnd.Pos,
  417.       dbname);
  418.   /* check the database */
  419.   if(NULL == dbname){
  420.     waislog(WLOG_HIGH, WLOG_WARNING,
  421.         "Missing database for doc-id: '%s'", local_id);
  422.     *errorCode = GDT_MissingDatabase;
  423.     s_free(local_id);
  424.     return(NULL);
  425.   }
  426.   
  427.   if(check_for_legitimate_file(filename, dbname, index_directory) == false){
  428.     waislog(WLOG_HIGH, WLOG_WARNING,
  429.         "doc-id: '%s' not in database '%s'", local_id,dbname);    
  430.     *errorCode = GDT_MissingDocID;
  431.     s_free(dbname);
  432.     s_free(local_id);
  433.     return(NULL);
  434.   }
  435.  
  436.   s_free(dbname);
  437.  
  438.   file = s_fopen(filename,"r");
  439.   if (file == NULL)
  440.     if(probe_file_possibly_compressed(filename)) {
  441.       char buffer[ 2 * MAX_FILENAME_LEN + 10 ];
  442. #if (defined(NeXT) || defined(Mach))
  443.       tmpnam(tmpFileName);
  444. #else
  445.       tmpFileName = tempnam( "/tmp/", 0 );
  446. #endif /* NeXT or Mach */
  447.       sprintf( buffer, "zcat %s.Z > %s", filename, tmpFileName );
  448.       system( buffer );
  449.       file = s_fopen(tmpFileName,"r");
  450.     }
  451.   if (file == NULL) { 
  452.     waislog(WLOG_HIGH, WLOG_WARNING, 
  453.         "Attempt to retrieve text for bad doc-id: '%s'", local_id);     
  454.     *errorCode = GDT_MissingDocID;
  455.     s_free(local_id);
  456.     return(NULL);
  457.   }
  458.  
  459.   if(0 != fseek(file, start_character, SEEK_SET))
  460.    { 
  461.      waislog(WLOG_HIGH, WLOG_WARNING, 
  462.          " error on attempt to seek into file for doc-id: '%s'", local_id);
  463.      s_free(local_id);
  464.      *errorCode = GDT_BadRange;
  465.      return(NULL);
  466.    }
  467.   /* find the start byte */
  468.   buffer = (char*)s_malloc(BUFSZ);
  469.   lines = byte = 0;
  470.   while (lines < doc->ChunkStart.Pos)
  471.    { /* search a buffer full */
  472.      bytesRead = fread(buffer,(size_t)sizeof(char),BUFSZ,file); 
  473.      for (i = 0; i < bytesRead && lines < doc->ChunkStart.Pos; i++, byte++)
  474.       { if (buffer[i] == '\n' || buffer[i] == '\r')
  475.       /* \r should not happen because we are reading the file in text 
  476.          mode */
  477.           lines++;
  478.       }
  479.      if (bytesRead == 0) /* cheasy handling files that don't end with nl */
  480.        lines++;
  481.    } 
  482.   startByte = byte;
  483.    
  484.   beFriendly();
  485.   
  486.   /* find the end byte */ /* this could be done while getting the bytes XXX */
  487.   /* search starting form the start pos */  
  488.   if (fseek(file,startByte + start_character,SEEK_SET) != 0) 
  489.    { 
  490.      waislog(WLOG_HIGH, WLOG_WARNING, 
  491.          "retrieval can't seek to %ld in file <%s>",
  492.          startByte,filename);
  493.      
  494.      *errorCode = GDT_BadRange;
  495.      if (tmpFileName) unlink( tmpFileName );
  496.      s_free(local_id);
  497.      return(NULL);
  498.    }
  499.  
  500.   beFriendly();
  501.   
  502.   while (lines < doc->ChunkEnd.Pos) 
  503.    { /* search a buffer full */
  504.      bytesRead = fread(buffer,(size_t)sizeof(char),BUFSZ,file); 
  505.      for (i = 0; i < bytesRead && lines < doc->ChunkEnd.Pos; i++, byte++)
  506.       { if (buffer[i] == '\n' || buffer[i] == '\r')
  507.       /* \r should not happen, we are reading the file in text mode */
  508.           lines++;
  509.       }
  510.      if (bytesRead == 0) /* cheasy handling of files that don't end with nl */
  511.        lines++;
  512.    } 
  513.   endByte = byte;
  514.    
  515.   beFriendly();
  516.   
  517.   s_free(buffer);
  518.      
  519.   /* get the bytes */
  520.   if (fseek(file,startByte + start_character,SEEK_SET) != 0)
  521.    { 
  522.      waislog(WLOG_HIGH, WLOG_WARNING, 
  523.          "retrieval can't seek to %ld in file <%s>",startByte,
  524.          filename);
  525.      
  526.      *errorCode = GDT_BadRange;
  527.      if (tmpFileName) unlink( tmpFileName );
  528.      s_free(local_id);
  529.      return(NULL);
  530.    }
  531.    
  532.   bytes = endByte - startByte; 
  533.   buffer = (char*)s_malloc(bytes);
  534.   
  535.   bytesRead = fread((void*)buffer,(size_t)sizeof(char),bytes,file);
  536.   
  537.   if (bytesRead != bytes)
  538.    { 
  539.      waislog(WLOG_HIGH, WLOG_WARNING, 
  540.          "retrieval error in file <%s>",filename);
  541.      
  542.      *errorCode = GDT_BadRange;
  543.      if (tmpFileName) unlink( tmpFileName );
  544.      s_free(local_id);
  545.      return(NULL);
  546.    }
  547.   
  548.   bufAny = makeAny(bytesRead,buffer);
  549.   
  550.   text = makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  551.   
  552.   /* the any and the buffer are freed by freeWAISSearchResponse() */
  553.   s_fclose(file);
  554.   if (tmpFileName) unlink( tmpFileName );
  555.   *errorCode = GDT_NoError;
  556.   s_free(local_id);
  557.   return(text);
  558. }
  559.